package com.wpml.netty.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * @Description 单线程selector模式
 * @Date 2021-07-16
 * @Author wangpei
 **/
public class SingleThreadSelectorTest {

    ServerSocketChannel server = null;
    Selector selector = null;
    int port = 9090;
    String address = "127.0.0.1";

    public static void main(String[] args) {
        SingleThreadSelectorTest selectorTest = new SingleThreadSelectorTest();
        selectorTest.startServer();
    }

    public void initServer() {
        try {
            /*selector = Selector.open();

            server = ServerSocketChannel.open();
            server.configureBlocking(false);// 设置非阻塞
            server.bind(new InetSocketAddress(port));

            server.register(selector, SelectionKey.OP_ACCEPT); // 注册到selector*/


            server = ServerSocketChannel.open();
            server.configureBlocking(false);
            server.bind(new InetSocketAddress(port));
            selector = Selector.open();  //  select  poll  *epoll
            server.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void startServer() {
        initServer();
        System.out.println("服务器启动了。。。。。");

        try {
            while (true) {
                System.out.println("before this.selector.select() " + selector.keys().size());
                int num = selector.select(); // 监控selector上是否有事件
                System.out.println("after this.selector.select() " + selector.keys().size());
                if (num > 0) {
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        iterator.remove();
                        if (key.isAcceptable()) {
                            acceptHandler(key);
                        } else if (key.isReadable()) {
                            readHandler(key);
                        } else if (key.isWritable()) {
                            writeHandler(key);
                        } else {
                            key.cancel();
                            System.out.println("链接断开。。。");
                        }
                    }
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*private void readHandler(SelectionKey key) {
        System.out.println("read handler.....");
        SocketChannel client = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        buffer.clear();
        int read = 0;
        try {
            while (true) {
                read = client.read(buffer);
                if (read > 0) {
                    client.register(key.selector(), SelectionKey.OP_WRITE, buffer);
                    //关心  OP_WRITE 其实就是关系send-queue是不是有空间
                } else if (read == 0) {
                    break;
                } else {
                    client.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }*/

    private void readHandler(SelectionKey key) {
        System.out.println("read handler.....");
        int read = 0;
        try {
            SocketChannel client = (SocketChannel) key.channel();
            System.out.println("readHandler " + client.getRemoteAddress());
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            System.out.println("buffer " + buffer.hasRemaining());
            buffer.clear();

            /*ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            client.register(key.selector(), SelectionKey.OP_READ,readBuffer);*/

            while (true) {
                read = client.read(buffer);
                System.out.println("read " + read);
                if (read > 0) {
                    buffer.flip();
                    byte[] bytes = new byte[buffer.limit()];
                    buffer.get(bytes,0, read);

                    System.out.println(new String(bytes, 0, read, StandardCharsets.UTF_8) + " ======= ");

                } else if (read == 0) {
                    System.out.println("未读取到数据...");
                    break;
                } else {
                    System.out.println("client : " + client.getRemoteAddress() + " 断开了......");
                    key.cancel();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void acceptHandler(SelectionKey key) {
        try {
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            SocketChannel client = ssc.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ, ByteBuffer.allocateDirect(4096));
            System.out.println("-------------------------------------------");
            System.out.println("新客户端：" + client.getRemoteAddress());
            System.out.println("-------------------------------------------");
//            selector.wakeup();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void writeHandler(SelectionKey key) {

        System.out.println("write handler...");
        SocketChannel client = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        buffer.flip();
        while (buffer.hasRemaining()) {
            try {

                client.write(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        buffer.clear();
        key.cancel();
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }


}
